﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using System.Xml;
using System.Xml.Linq;
using ActionLogic;
using BehaviorModel;
using BehaviorModel.Block;
using BehaviorModel.Statistic;
using CommandLogic;
using CommandLogic.Command;
using CommandLogic.Data;
using Microsoft.Win32;
using QSDesigner.Windows;

namespace QSDesigner
{
    public partial class DesignerCanvas
    {
        /*public static RoutedCommand Group = new RoutedCommand();
        public static RoutedCommand Ungroup = new RoutedCommand();
        public static RoutedCommand BringForward = new RoutedCommand();
        public static RoutedCommand BringToFront = new RoutedCommand();
        public static RoutedCommand SendBackward = new RoutedCommand();
        public static RoutedCommand SendToBack = new RoutedCommand();
        public static RoutedCommand AlignTop = new RoutedCommand();
        public static RoutedCommand AlignVerticalCenters = new RoutedCommand();
        public static RoutedCommand AlignBottom = new RoutedCommand();
        public static RoutedCommand AlignLeft = new RoutedCommand();
        public static RoutedCommand AlignHorizontalCenters = new RoutedCommand();
        public static RoutedCommand AlignRight = new RoutedCommand();
        public static RoutedCommand DistributeHorizontal = new RoutedCommand();
        public static RoutedCommand DistributeVertical = new RoutedCommand();
        public static RoutedCommand SelectAll = new RoutedCommand();*/

        public static RoutedCommand Start = new RoutedCommand();
        public static RoutedCommand Pause = new RoutedCommand();
        public static RoutedCommand Stop = new RoutedCommand();

        public static RoutedCommand ShowProperties = new RoutedCommand();

        public static RoutedCommand ShowHistogram = new RoutedCommand();

        public static RoutedCommand GridOn = new RoutedCommand();

        public DesignerCanvas()
        {
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.New, New_Executed));
            /* this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Open, Open_Executed, Open_Enabled));
             this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, Save_Executed, Save_Enabled));
             this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Print, Print_Executed));
             this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Cut, Cut_Executed, Cut_Enabled));
             this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Copy, Copy_Executed, Copy_Enabled));
             this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste, Paste_Executed, Paste_Enabled));*/
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Delete, Delete_Executed, Delete_Enabled));
            /*this.CommandBindings.Add(new CommandBinding(DesignerCanvas.Group, Group_Executed, Group_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.Ungroup, Ungroup_Executed, Ungroup_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.BringForward, BringForward_Executed, Order_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.BringToFront, BringToFront_Executed, Order_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.SendBackward, SendBackward_Executed, Order_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.SendToBack, SendToBack_Executed, Order_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.AlignTop, AlignTop_Executed, Align_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.AlignVerticalCenters, AlignVerticalCenters_Executed, Align_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.AlignBottom, AlignBottom_Executed, Align_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.AlignLeft, AlignLeft_Executed, Align_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.AlignHorizontalCenters, AlignHorizontalCenters_Executed, Align_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.AlignRight, AlignRight_Executed, Align_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.DistributeHorizontal, DistributeHorizontal_Executed, Distribute_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.DistributeVertical, DistributeVertical_Executed, Distribute_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.SelectAll, SelectAll_Executed));*/

            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.Start, Start_Executed, Start_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.Pause, Pause_Executed, Pause_Enabled));
            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.Stop, Stop_Executed, Stop_Enabled));

            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.ShowProperties, ShowProperties_Executed, ShowProperties_Enabled));
            //this.CommandBindings.Add(new CommandBinding(DesignerCanvas.ShowHistogram, ShowHistogram_Executed, ShowHistogram_Enabled));

            this.CommandBindings.Add(new CommandBinding(DesignerCanvas.GridOn, GridOn_Executed, GridOn_Enabled));
            /*
                        SelectAll.InputGestures.Add(new KeyGesture(Key.A, ModifierKeys.Control));*/

            this.AllowDrop = true;

            Clipboard.Clear();
        }

        #region New Command

        private void New_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            this.Children.Clear();
            this.SelectionService.ClearSelection();
        }

        #endregion
        /*

        #region Open Command

        private void Open_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            XElement root = LoadSerializedDataFromFile();

            if (root == null)
                return;

            this.Children.Clear();
            this.SelectionService.ClearSelection();

            IEnumerable<XElement> itemsXML = root.Elements("DesignerItems").Elements("DesignerItem");
            foreach (XElement itemXML in itemsXML)
            {
                Guid id = new Guid(itemXML.Element("ID").Value);
                DesignerItem item = DeserializeDesignerItem(itemXML, id, 0, 0);
                this.Children.Add(item);
                SetConnectorDecoratorTemplate(item);
            }

            this.InvalidateVisual();

            IEnumerable<XElement> connectionsXML = root.Elements("Connections").Elements("Connection");
            foreach (XElement connectionXML in connectionsXML)
            {
                Guid sourceID = new Guid(connectionXML.Element("SourceID").Value);
                Guid sinkID = new Guid(connectionXML.Element("SinkID").Value);

                String sourceConnectorName = connectionXML.Element("SourceConnectorName").Value;
                String sinkConnectorName = connectionXML.Element("SinkConnectorName").Value;

                Connector sourceConnector = GetConnector(sourceID, sourceConnectorName);
                Connector sinkConnector = GetConnector(sinkID, sinkConnectorName);

                Connection connection = new Connection(sourceConnector, sinkConnector);
                Canvas.SetZIndex(connection, Int32.Parse(connectionXML.Element("zIndex").Value));
                this.Children.Add(connection);
            }
        }

        private void Open_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = false;
        }

        #endregion

        #region Save Command

        private void Save_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            IEnumerable<DesignerItem> designerItems = this.Children.OfType<DesignerItem>();
            IEnumerable<Connection> connections = this.Children.OfType<Connection>();

            XElement designerItemsXML = SerializeDesignerItems(designerItems);
            XElement connectionsXML = SerializeConnections(connections);

            XElement root = new XElement("Root");
            root.Add(designerItemsXML);
            root.Add(connectionsXML);

            SaveFile(root);
        }

        private void Save_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = false;
        }

        #endregion

        #region Print Command

        private void Print_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            SelectionService.ClearSelection();

            PrintDialog printDialog = new PrintDialog();

            if (true == printDialog.ShowDialog())
            {
                printDialog.PrintVisual(this, "WPF Diagram");
            }
        }

        #endregion

        #region Copy Command

        private void Copy_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            CopyCurrentSelection();
        }

        private void Copy_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            //e.CanExecute = SelectionService.CurrentSelection.Count() > 0;
            e.CanExecute = false;
        }

        #endregion

        #region Paste Command

        private void Paste_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            XElement root = LoadSerializedDataFromClipBoard();

            if (root == null)
                return;

            // create DesignerItems
            Dictionary<Guid, Guid> mappingOldToNewIDs = new Dictionary<Guid, Guid>();
            List<ISelectable> newItems = new List<ISelectable>();
            IEnumerable<XElement> itemsXML = root.Elements("DesignerItems").Elements("DesignerItem");

            double offsetX = Double.Parse(root.Attribute("OffsetX").Value, CultureInfo.InvariantCulture);
            double offsetY = Double.Parse(root.Attribute("OffsetY").Value, CultureInfo.InvariantCulture);

            foreach (XElement itemXML in itemsXML)
            {
                Guid oldID = new Guid(itemXML.Element("ID").Value);
                Guid newID = Guid.NewGuid();
                mappingOldToNewIDs.Add(oldID, newID);
                DesignerItem item = DeserializeDesignerItem(itemXML, newID, offsetX, offsetY);
                this.Children.Add(item);
                SetConnectorDecoratorTemplate(item);
                newItems.Add(item);
            }

            // update group hierarchy
            SelectionService.ClearSelection();
            foreach (DesignerItem el in newItems)
            {
                if (el.ParentID != Guid.Empty)
                    el.ParentID = mappingOldToNewIDs[el.ParentID];
            }


            foreach (DesignerItem item in newItems)
            {
                if (item.ParentID == Guid.Empty)
                {
                    SelectionService.AddToSelection(item);
                }
            }

            // create Connections
            IEnumerable<XElement> connectionsXML = root.Elements("Connections").Elements("Connection");
            foreach (XElement connectionXML in connectionsXML)
            {
                Guid oldSourceID = new Guid(connectionXML.Element("SourceID").Value);
                Guid oldSinkID = new Guid(connectionXML.Element("SinkID").Value);

                if (mappingOldToNewIDs.ContainsKey(oldSourceID) && mappingOldToNewIDs.ContainsKey(oldSinkID))
                {
                    Guid newSourceID = mappingOldToNewIDs[oldSourceID];
                    Guid newSinkID = mappingOldToNewIDs[oldSinkID];

                    String sourceConnectorName = connectionXML.Element("SourceConnectorName").Value;
                    String sinkConnectorName = connectionXML.Element("SinkConnectorName").Value;

                    Connector sourceConnector = GetConnector(newSourceID, sourceConnectorName);
                    Connector sinkConnector = GetConnector(newSinkID, sinkConnectorName);

                    Connection connection = new Connection(sourceConnector, sinkConnector);
                    Canvas.SetZIndex(connection, Int32.Parse(connectionXML.Element("zIndex").Value));
                    this.Children.Add(connection);

                    SelectionService.AddToSelection(connection);
                }
            }

            DesignerCanvas.BringToFront.Execute(null, this);

            // update paste offset
            root.Attribute("OffsetX").Value = (offsetX + 10).ToString();
            root.Attribute("OffsetY").Value = (offsetY + 10).ToString();
            Clipboard.Clear();
            Clipboard.SetData(DataFormats.Xaml, root);
        }

        private void Paste_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            //e.CanExecute = Clipboard.ContainsData(DataFormats.Xaml);
            e.CanExecute = false;
        }

        #endregion
*/

        #region Delete Command

        private void Delete_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            DeleteCurrentSelection();
        }

        private void Delete_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = this.SelectionService.CurrentSelection.Count() > 0;
        }

        #endregion

        /*   #region Cut Command

        private void Cut_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            CopyCurrentSelection();
            DeleteCurrentSelection();
        }

        private void Cut_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            //e.CanExecute = this.SelectionService.CurrentSelection.Count() > 0;
            e.CanExecute = false;
        }


        #endregion

        #region Group Command

        private void Group_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var items = from item in this.SelectionService.CurrentSelection.OfType<DesignerItem>()
                        where item.ParentID == Guid.Empty
                        select item;

            Rect rect = GetBoundingRectangle(items);

            DesignerItem groupItem = new DesignerItem();
            groupItem.IsGroup = true;
            groupItem.Width = rect.Width;
            groupItem.Height = rect.Height;
            Canvas.SetLeft(groupItem, rect.Left);
            Canvas.SetTop(groupItem, rect.Top);
            Canvas groupCanvas = new Canvas();
            groupItem.Content = groupCanvas;
            Canvas.SetZIndex(groupItem, this.Children.Count);
            this.Children.Add(groupItem);

            foreach (DesignerItem item in items)
                item.ParentID = groupItem.ID;

            this.SelectionService.SelectItem(groupItem);
        }

        private void Group_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            int count = (from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
                         where item.ParentID == Guid.Empty
                         select item).Count();

            e.CanExecute = count > 1;
        }

        #endregion

        #region Ungroup Command

        private void Ungroup_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var groups = (from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
                          where item.IsGroup && item.ParentID == Guid.Empty
                          select item).ToArray();

            foreach (DesignerItem groupRoot in groups)
            {
                var children = from child in SelectionService.CurrentSelection.OfType<DesignerItem>()
                               where child.ParentID == groupRoot.ID
                               select child;

                foreach (DesignerItem child in children)
                    child.ParentID = Guid.Empty;

                this.SelectionService.RemoveFromSelection(groupRoot);
                this.Children.Remove(groupRoot);
                UpdateZIndex();
            }
        }

        private void Ungroup_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            var groupedItem = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
                              where item.ParentID != Guid.Empty
                              select item;


            e.CanExecute = groupedItem.Count() > 0;
        }

        #endregion

        #region BringForward Command

        private void BringForward_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            List<UIElement> ordered = (from item in SelectionService.CurrentSelection
                                       orderby Canvas.GetZIndex(item as UIElement) descending
                                       select item as UIElement).ToList();

            int count = this.Children.Count;

            for (int i = 0; i < ordered.Count; i++)
            {
                int currentIndex = Canvas.GetZIndex(ordered[i]);
                int newIndex = Math.Min(count - 1 - i, currentIndex + 1);
                if (currentIndex != newIndex)
                {
                    Canvas.SetZIndex(ordered[i], newIndex);
                    IEnumerable<UIElement> it = this.Children.OfType<UIElement>().Where(item => Canvas.GetZIndex(item) == newIndex);

                    foreach (UIElement elm in it)
                    {
                        if (elm != ordered[i])
                        {
                            Canvas.SetZIndex(elm, currentIndex);
                            break;
                        }
                    }
                }
            }
        }

        private void Order_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            //e.CanExecute = SelectionService.CurrentSelection.Count() > 0;
            e.CanExecute = true;
        }

        #endregion

        #region BringToFront Command

        private void BringToFront_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            List<UIElement> selectionSorted = (from item in SelectionService.CurrentSelection
                                               orderby Canvas.GetZIndex(item as UIElement) ascending
                                               select item as UIElement).ToList();

            List<UIElement> childrenSorted = (from UIElement item in this.Children
                                              orderby Canvas.GetZIndex(item as UIElement) ascending
                                              select item as UIElement).ToList();

            int i = 0;
            int j = 0;
            foreach (UIElement item in childrenSorted)
            {
                if (selectionSorted.Contains(item))
                {
                    int idx = Canvas.GetZIndex(item);
                    Canvas.SetZIndex(item, childrenSorted.Count - selectionSorted.Count + j++);
                }
                else
                {
                    Canvas.SetZIndex(item, i++);
                }
            }
        }

        #endregion

        #region SendBackward Command

        private void SendBackward_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            List<UIElement> ordered = (from item in SelectionService.CurrentSelection
                                       orderby Canvas.GetZIndex(item as UIElement) ascending
                                       select item as UIElement).ToList();

            int count = this.Children.Count;

            for (int i = 0; i < ordered.Count; i++)
            {
                int currentIndex = Canvas.GetZIndex(ordered[i]);
                int newIndex = Math.Max(i, currentIndex - 1);
                if (currentIndex != newIndex)
                {
                    Canvas.SetZIndex(ordered[i], newIndex);
                    IEnumerable<UIElement> it = this.Children.OfType<UIElement>().Where(item => Canvas.GetZIndex(item) == newIndex);

                    foreach (UIElement elm in it)
                    {
                        if (elm != ordered[i])
                        {
                            Canvas.SetZIndex(elm, currentIndex);
                            break;
                        }
                    }
                }
            }
        }

        #endregion

        #region SendToBack Command

        private void SendToBack_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            List<UIElement> selectionSorted = (from item in SelectionService.CurrentSelection
                                               orderby Canvas.GetZIndex(item as UIElement) ascending
                                               select item as UIElement).ToList();

            List<UIElement> childrenSorted = (from UIElement item in this.Children
                                              orderby Canvas.GetZIndex(item as UIElement) ascending
                                              select item as UIElement).ToList();
            int i = 0;
            int j = 0;
            foreach (UIElement item in childrenSorted)
            {
                if (selectionSorted.Contains(item))
                {
                    int idx = Canvas.GetZIndex(item);
                    Canvas.SetZIndex(item, j++);

                }
                else
                {
                    Canvas.SetZIndex(item, selectionSorted.Count + i++);
                }
            }
        }

        #endregion

        #region AlignTop Command

        private void AlignTop_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
                                where item.ParentID == Guid.Empty
                                select item;

            if (selectedItems.Count() > 1)
            {
                double top = Canvas.GetTop(selectedItems.First());

                foreach (DesignerItem item in selectedItems)
                {
                    double delta = top - Canvas.GetTop(item);
                    foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
                    {
                        Canvas.SetTop(di, Canvas.GetTop(di) + delta);
                    }
                }
            }
        }

        private void Align_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            //var groupedItem = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
            //                  where item.ParentID == Guid.Empty
            //                  select item;


            //e.CanExecute = groupedItem.Count() > 1;
            e.CanExecute = true;
        }

        #endregion

        #region AlignVerticalCenters Command

        private void AlignVerticalCenters_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
                                where item.ParentID == Guid.Empty
                                select item;

            if (selectedItems.Count() > 1)
            {
                double bottom = Canvas.GetTop(selectedItems.First()) + selectedItems.First().Height / 2;

                foreach (DesignerItem item in selectedItems)
                {
                    double delta = bottom - (Canvas.GetTop(item) + item.Height / 2);
                    foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
                    {
                        Canvas.SetTop(di, Canvas.GetTop(di) + delta);
                    }
                }
            }
        }

        #endregion

        #region AlignBottom Command

        private void AlignBottom_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
                                where item.ParentID == Guid.Empty
                                select item;

            if (selectedItems.Count() > 1)
            {
                double bottom = Canvas.GetTop(selectedItems.First()) + selectedItems.First().Height;

                foreach (DesignerItem item in selectedItems)
                {
                    double delta = bottom - (Canvas.GetTop(item) + item.Height);
                    foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
                    {
                        Canvas.SetTop(di, Canvas.GetTop(di) + delta);
                    }
                }
            }
        }

        #endregion

        #region AlignLeft Command

        private void AlignLeft_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
                                where item.ParentID == Guid.Empty
                                select item;

            if (selectedItems.Count() > 1)
            {
                double left = Canvas.GetLeft(selectedItems.First());

                foreach (DesignerItem item in selectedItems)
                {
                    double delta = left - Canvas.GetLeft(item);
                    foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
                    {
                        Canvas.SetLeft(di, Canvas.GetLeft(di) + delta);
                    }
                }
            }
        }

        #endregion

        #region AlignHorizontalCenters Command

        private void AlignHorizontalCenters_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
                                where item.ParentID == Guid.Empty
                                select item;

            if (selectedItems.Count() > 1)
            {
                double center = Canvas.GetLeft(selectedItems.First()) + selectedItems.First().Width / 2;

                foreach (DesignerItem item in selectedItems)
                {
                    double delta = center - (Canvas.GetLeft(item) + item.Width / 2);
                    foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
                    {
                        Canvas.SetLeft(di, Canvas.GetLeft(di) + delta);
                    }
                }
            }
        }

        #endregion

        #region AlignRight Command

        private void AlignRight_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
                                where item.ParentID == Guid.Empty
                                select item;

            if (selectedItems.Count() > 1)
            {
                double right = Canvas.GetLeft(selectedItems.First()) + selectedItems.First().Width;

                foreach (DesignerItem item in selectedItems)
                {
                    double delta = right - (Canvas.GetLeft(item) + item.Width);
                    foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
                    {
                        Canvas.SetLeft(di, Canvas.GetLeft(di) + delta);
                    }
                }
            }
        }

        #endregion

        #region DistributeHorizontal Command

        private void DistributeHorizontal_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
                                where item.ParentID == Guid.Empty
                                let itemLeft = Canvas.GetLeft(item)
                                orderby itemLeft
                                select item;

            if (selectedItems.Count() > 1)
            {
                double left = Double.MaxValue;
                double right = Double.MinValue;
                double sumWidth = 0;
                foreach (DesignerItem item in selectedItems)
                {
                    left = Math.Min(left, Canvas.GetLeft(item));
                    right = Math.Max(right, Canvas.GetLeft(item) + item.Width);
                    sumWidth += item.Width;
                }

                double distance = Math.Max(0, (right - left - sumWidth) / (selectedItems.Count() - 1));
                double offset = Canvas.GetLeft(selectedItems.First());

                foreach (DesignerItem item in selectedItems)
                {
                    double delta = offset - Canvas.GetLeft(item);
                    foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
                    {
                        Canvas.SetLeft(di, Canvas.GetLeft(di) + delta);
                    }
                    offset = offset + item.Width + distance;
                }
            }
        }

        private void Distribute_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            //var groupedItem = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
            //                  where item.ParentID == Guid.Empty
            //                  select item;


            //e.CanExecute = groupedItem.Count() > 1;
            e.CanExecute = true;
        }

        #endregion

        #region DistributeVertical Command

        private void DistributeVertical_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var selectedItems = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
                                where item.ParentID == Guid.Empty
                                let itemTop = Canvas.GetTop(item)
                                orderby itemTop
                                select item;

            if (selectedItems.Count() > 1)
            {
                double top = Double.MaxValue;
                double bottom = Double.MinValue;
                double sumHeight = 0;
                foreach (DesignerItem item in selectedItems)
                {
                    top = Math.Min(top, Canvas.GetTop(item));
                    bottom = Math.Max(bottom, Canvas.GetTop(item) + item.Height);
                    sumHeight += item.Height;
                }

                double distance = Math.Max(0, (bottom - top - sumHeight) / (selectedItems.Count() - 1));
                double offset = Canvas.GetTop(selectedItems.First());

                foreach (DesignerItem item in selectedItems)
                {
                    double delta = offset - Canvas.GetTop(item);
                    foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
                    {
                        Canvas.SetTop(di, Canvas.GetTop(di) + delta);
                    }
                    offset = offset + item.Height + distance;
                }
            }
        }

        #endregion

        #region SelectAll Command

        private void SelectAll_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            SelectionService.SelectAll();
        }

        #endregion
*/

        #region Start Command

        private void Start_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            //Start statistics
            var startCommand = QsmCommandFactory.GetCommand(QsmCommand.Start);
            startCommand.Execute(null);
            RunDispatcher();
        }

        private void Start_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = ActionExecutor.GetInstance().ActionExecutorState != ActionExecutorState.Start;
        }

        #endregion

        #region Pause Command

        private void Pause_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            //Pause statistics
            var startCommand = QsmCommandFactory.GetCommand(QsmCommand.Pause);
            startCommand.Execute(null);
            _dispatcherTimer.Stop();
        }

        private void Pause_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = ActionExecutor.GetInstance().ActionExecutorState == ActionExecutorState.Start;
        }

        #endregion

        #region Stop Command

        private void Stop_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            //Pause statistics
            var startCommand = QsmCommandFactory.GetCommand(QsmCommand.Stop);
            startCommand.Execute(null);
            _dispatcherTimer.Stop();
        }

        private void Stop_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = ActionExecutor.GetInstance().ActionExecutorState == ActionExecutorState.Start;
        }

        #endregion




        #region ShowProperties

        private void ShowProperties_Executed(object sender, ExecutedRoutedEventArgs e)
        {

            var toolboxImage = sender as DesignerCanvas;

            if (toolboxImage != null)
            {
                var selectionService = toolboxImage.selectionService;

                var designerItem = selectionService.CurrentSelection[0] as DesignerItem;

                if (designerItem.Block.Type != BlockType.Summator)
                {
                    var window = new PropertiesWindow { Item = designerItem };
                    window.Show();
                }

                //WindowManager.LaunchWindowNewThread<PropertiesWindow>(designerItem/*, (o, args) => ((PropertiesWindow) o).Init()*/);

            }

            e.Handled = true;
        }

        private void ShowProperties_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        #endregion

        /*#region ShowHistogram

        private void ShowHistogram_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var toolboxImage = sender as DesignerCanvas;

            if (toolboxImage != null)
            {
                var selectionService = toolboxImage.selectionService;

                var designerItem = selectionService.CurrentSelection[0] as DesignerItem;
                
                //TODO There need to check for checkers correctly
                
                var checkers = new List<string> {"CHECKER_UNIFORM", "CHECKER_EXPONENTIAL", "CHECKER_GAUSSIAN"};
                
                if (designerItem != null && checkers.Contains(designerItem.Block.Type))
                {
                    WindowManager.LaunchWindowNewThread<CheckerWindow>(designerItem);
                }
            }

            e.Handled = true;
        }

        private void ShowHistogram_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        #endregion*/


        #region GridOn

        private void GridOn_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            /*var contextMenu = (ContextMenu)FindResource("DesignerCanvasContextMenu");

            foreach (MenuItem menuItem in contextMenu.Items)
            {
                if (menuItem.Name == "DesignerCanvasGrid")
                {
                    if (!menuItem.)
                    {
                        ImageBrush myBrush = new ImageBrush();
                        Image image = new Image
                        {
                            Source = new BitmapImage(
                                new Uri(
                                    "pack://application:,,,/QSDesigner;component/Resources/Images/gridIcon.png"))
                        };
                        myBrush.ImageSource = image.Source;
                        myBrush.Viewport = new Rect(0, 0, 16, 16);
                        myBrush.TileMode = TileMode.Tile;
                        myBrush.ViewportUnits = BrushMappingMode.Absolute;

                        this.Background = myBrush;
                    }
                    else
                    {
                        this.Background = Brushes.White;
                    }
                }
            }*/

            ImageBrush myBrush = new ImageBrush();
            Image image = new Image
            {
                Source = new BitmapImage(
                    new Uri(
                        "pack://application:,,,/QSDesigner;component/Resources/Images/gridIcon.png"))
            };
            myBrush.ImageSource = image.Source;
            myBrush.Viewport = new Rect(0, 0, 16, 16);
            myBrush.TileMode = TileMode.Tile;
            myBrush.ViewportUnits = BrushMappingMode.Absolute;

            this.Background = myBrush;

            e.Handled = true;
        }

        private void GridOn_Enabled(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }


        #endregion


        #region Helper Methods

        private XElement LoadSerializedDataFromFile()
        {
            OpenFileDialog openFile = new OpenFileDialog();
            openFile.Filter = "Designer Files (*.xml)|*.xml|All Files (*.*)|*.*";

            if (openFile.ShowDialog() == true)
            {
                try
                {
                    return XElement.Load(openFile.FileName);
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.StackTrace, e.Message, MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }

            return null;
        }

        void SaveFile(XElement xElement)
        {
            SaveFileDialog saveFile = new SaveFileDialog();
            saveFile.Filter = "Files (*.xml)|*.xml|All Files (*.*)|*.*";
            if (saveFile.ShowDialog() == true)
            {
                try
                {
                    xElement.Save(saveFile.FileName);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.StackTrace, ex.Message, MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        private XElement LoadSerializedDataFromClipBoard()
        {
            if (Clipboard.ContainsData(DataFormats.Xaml))
            {
                String clipboardData = Clipboard.GetData(DataFormats.Xaml) as String;

                if (String.IsNullOrEmpty(clipboardData))
                    return null;
                try
                {
                    return XElement.Load(new StringReader(clipboardData));
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.StackTrace, e.Message, MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }

            return null;
        }

        private XElement SerializeDesignerItems(IEnumerable<DesignerItem> designerItems)
        {
            XElement serializedItems = new XElement("DesignerItems",
                                       from item in designerItems
                                       let contentXaml = XamlWriter.Save(((DesignerItem)item).Content)
                                       select new XElement("DesignerItem",
                                                  new XElement("Left", Canvas.GetLeft(item)),
                                                  new XElement("Top", Canvas.GetTop(item)),
                                                  new XElement("Width", item.Width),
                                                  new XElement("Height", item.Height),
                                                  new XElement("ID", item.ID),
                                                  new XElement("Type", item.Block.Type),
                                                  new XElement("zIndex", Canvas.GetZIndex(item)),
                                                  new XElement("IsGroup", item.IsGroup),
                                                  new XElement("ParentID", item.ParentID),
                                                  new XElement("Content", contentXaml)
                                              )
                                   );

            return serializedItems;
        }

        private XElement SerializeConnections(IEnumerable<Connection> connections)
        {
            var serializedConnections = new XElement("Connections",
                           from connection in connections
                           select new XElement("Connection",
                                      new XElement("SourceID", connection.Source.ParentDesignerItem.ID),
                                      new XElement("SinkID", connection.Sink.ParentDesignerItem.ID),
                                      new XElement("SourceConnectorName", connection.Source.Name),
                                      new XElement("SinkConnectorName", connection.Sink.Name),
                                      new XElement("SourceArrowSymbol", connection.SourceArrowSymbol),
                                      new XElement("SinkArrowSymbol", connection.SinkArrowSymbol),
                                      new XElement("zIndex", Canvas.GetZIndex(connection))
                                     )
                                  );

            return serializedConnections;
        }

        private static DesignerItem DeserializeDesignerItem(XElement itemXML, Guid id, double OffsetX, double OffsetY)
        {
            DesignerItem item = new DesignerItem(id);
            item.Width = Double.Parse(itemXML.Element("Width").Value, CultureInfo.InvariantCulture);
            item.Height = Double.Parse(itemXML.Element("Height").Value, CultureInfo.InvariantCulture);
            item.ParentID = new Guid(itemXML.Element("ParentID").Value);
            item.IsGroup = Boolean.Parse(itemXML.Element("IsGroup").Value);
            Canvas.SetLeft(item, Double.Parse(itemXML.Element("Left").Value, CultureInfo.InvariantCulture) + OffsetX);
            Canvas.SetTop(item, Double.Parse(itemXML.Element("Top").Value, CultureInfo.InvariantCulture) + OffsetY);
            Canvas.SetZIndex(item, Int32.Parse(itemXML.Element("zIndex").Value));
            Object content = XamlReader.Load(XmlReader.Create(new StringReader(itemXML.Element("Content").Value)));
            item.Content = content;
            return item;
        }

        private void CopyCurrentSelection()
        {
            IEnumerable<DesignerItem> selectedDesignerItems =
                this.SelectionService.CurrentSelection.OfType<DesignerItem>();

            List<Connection> selectedConnections =
                this.SelectionService.CurrentSelection.OfType<Connection>().ToList();

            foreach (Connection connection in this.Children.OfType<Connection>())
            {
                if (!selectedConnections.Contains(connection))
                {
                    DesignerItem sourceItem = (from item in selectedDesignerItems
                                               where item.ID == connection.Source.ParentDesignerItem.ID
                                               select item).FirstOrDefault();

                    DesignerItem sinkItem = (from item in selectedDesignerItems
                                             where item.ID == connection.Sink.ParentDesignerItem.ID
                                             select item).FirstOrDefault();

                    if (sourceItem != null &&
                        sinkItem != null &&
                        BelongToSameGroup(sourceItem, sinkItem))
                    {
                        selectedConnections.Add(connection);
                    }
                }
            }

            XElement designerItemsXML = SerializeDesignerItems(selectedDesignerItems);
            XElement connectionsXML = SerializeConnections(selectedConnections);

            XElement root = new XElement("Root");
            root.Add(designerItemsXML);
            root.Add(connectionsXML);

            root.Add(new XAttribute("OffsetX", 10));
            root.Add(new XAttribute("OffsetY", 10));

            Clipboard.Clear();
            Clipboard.SetData(DataFormats.Xaml, root);
        }

        private void DeleteCurrentSelection()
        {
            var disconnectBlocksCommand = QsmCommandFactory.GetCommand(QsmCommand.DisconnectBlocks);
            var deleteBlockCommand = QsmCommandFactory.GetCommand(QsmCommand.DeleteBlock);
           
            foreach (Connection connection in SelectionService.CurrentSelection.OfType<Connection>())
            {
                this.Children.Remove(connection);
                //Disconnect blocks
                var commandData = new SenderReceiverData
                {
                    SenderBlock = (ISenderBlock)connection.Source.ParentDesignerItem.Block,
                    RecieverBlock = (IReceiverBlock)connection.Sink.ParentDesignerItem.Block
                };
                disconnectBlocksCommand.Execute(commandData);
            }

            foreach (DesignerItem item in SelectionService.CurrentSelection.OfType<DesignerItem>())
            {
                Control cd = item.Template.FindName("PART_ConnectorDecorator", item) as Control;

                List<Connector> connectors = new List<Connector>();
                GetConnectors(cd, connectors);

                foreach (Connector connector in connectors)
                {
                    foreach (Connection con in connector.Connections)
                    {
                        this.Children.Remove(con);
                        //Disconnect blocks
                        /*var commandData = new SenderRecieverData
                        {
                            SenderBlock = (ISenderBlock)con.Source.ParentDesignerItem.Block,
                            RecieverBlock = (IReceiverBlock)con.Sink.ParentDesignerItem.Block
                        };
                        disconnectBlocksCommand.Execute(commandData); */                      
                    }
                }
                this.Children.Remove(item);

                //Delete item
                var blockData = new BlockData { Block = item.Block};
                deleteBlockCommand.Execute(blockData);
            }

            SelectionService.ClearSelection();
            UpdateZIndex();
        }

        private void UpdateZIndex()
        {
            List<UIElement> ordered = (from UIElement item in this.Children
                                       orderby Canvas.GetZIndex(item as UIElement)
                                       select item as UIElement).ToList();

            for (int i = 0; i < ordered.Count; i++)
            {
                Canvas.SetZIndex(ordered[i], i);
            }
        }

        private static Rect GetBoundingRectangle(IEnumerable<DesignerItem> items)
        {
            double x1 = Double.MaxValue;
            double y1 = Double.MaxValue;
            double x2 = Double.MinValue;
            double y2 = Double.MinValue;

            foreach (DesignerItem item in items)
            {
                x1 = Math.Min(Canvas.GetLeft(item), x1);
                y1 = Math.Min(Canvas.GetTop(item), y1);

                x2 = Math.Max(Canvas.GetLeft(item) + item.Width, x2);
                y2 = Math.Max(Canvas.GetTop(item) + item.Height, y2);
            }

            return new Rect(new Point(x1, y1), new Point(x2, y2));
        }

        private void GetConnectors(DependencyObject parent, List<Connector> connectors)
        {
            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childrenCount; i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(parent, i);
                if (child is Connector)
                {
                    connectors.Add(child as Connector);
                }
                else
                    GetConnectors(child, connectors);
            }
        }

        private Connector GetConnector(Guid itemID, String connectorName)
        {
            DesignerItem designerItem = (from item in this.Children.OfType<DesignerItem>()
                                         where item.ID == itemID
                                         select item).FirstOrDefault();

            Control connectorDecorator = designerItem.Template.FindName("PART_ConnectorDecorator", designerItem) as Control;
            connectorDecorator.ApplyTemplate();

            return connectorDecorator.Template.FindName(connectorName, connectorDecorator) as Connector;
        }

        private bool BelongToSameGroup(IGroupable item1, IGroupable item2)
        {
            IGroupable root1 = SelectionService.GetGroupRoot(item1);
            IGroupable root2 = SelectionService.GetGroupRoot(item2);

            return (root1.ID == root2.ID);
        }

        #region Additions

        private void RunDispatcher()
        {
            if (_dispatcherTimer != null)
            {
                _dispatcherTimer.Stop();

                _dispatcherTimer.Tick += (s, a) =>
                {
                    Action action;

                    if (IsWholeSystemStatisticsShowing)
                    {
                        action = UpdateSystemStatistic;
                    }
                    else
                    {
                        action = UpdateBlockStatistic;
                    }

                    Dispatcher.Invoke(action);
                };

                _dispatcherTimer.Start();

            }
            else
            {
                _dispatcherTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) };

                _dispatcherTimer.Tick += (s, a) =>
                {
                    Action action;

                    if (IsWholeSystemStatisticsShowing)
                    {
                        action = UpdateSystemStatistic;
                    }
                    else
                    {
                        action = UpdateBlockStatistic;
                    }

                    Dispatcher.Invoke(action);
                };

                _dispatcherTimer.Start();
            }
        }

        public void InitializeBlockStatisticsContentControl(DesignerItem item)
        {
            var resourceDictionary = new ResourceDictionary
                 {
                     Source =
                         new Uri(
                         "/QSDesigner;component/Resources/Properties/Statistics' Properties/BlockStatisticProperties.xaml",
                         UriKind.RelativeOrAbsolute)
                 };

            var contentControl = (ContentControl)FindName("StatisticProperties");

            if (contentControl != null)
            {
                contentControl.Content = resourceDictionary["BlockStatisticProperties"];

                var selectedBlock = (DesignerItem)SelectionService.CurrentSelection[0];

                var currentBlock = StatisticController.GetInstance();

                UpdateContentControlProperties(contentControl,
                                               currentBlock.Statistic != null
                                                   ? currentBlock.Statistic[selectedBlock.Block.Id]
                                                   : null, selectedBlock.Block.Type.ToString());

                IsWholeSystemStatisticsShowing = false;
            }

        }

        public void InitializeSystemStatisticsContentControl()
        {
            var resourceDictionary = new ResourceDictionary
                  {
                      Source =
                          new Uri(
                          "/QSDesigner;component/Resources/Properties/Statistics' Properties/SystemStatisticProperties.xaml",
                          UriKind.RelativeOrAbsolute)
                  };


            var contentControl = (ContentControl)FindName("StatisticProperties");

            if (contentControl != null)
            {
                contentControl.Content = resourceDictionary["SystemStatisticProperties"];

                if (StatisticController.GetInstance().Statistic != null)
                {
                    var updateStatisticCommand = new UpdateStatisticCommand();
                    updateStatisticCommand.Execute(null);

                    var groupBox = contentControl.Content as GroupBox;
                    if (groupBox != null)
                    {
                        groupBox.DataContext = StatisticController.GetInstance().Statistic;
                    }
                }

                IsWholeSystemStatisticsShowing = true;
            }

        }


        public void UpdateBlockStatistic()
        {
            var state = ActionExecutor.GetInstance().ActionExecutorState;
            if (state == ActionExecutorState.Start || state == ActionExecutorState.Pause && SelectionService.CurrentSelection.Count != 0)
            {
                var updateStatisticCommand = QsmCommandFactory.GetCommand(QsmCommand.UpdateStatistic);
                updateStatisticCommand.Execute(null);
                var selectedBlock = (DesignerItem)SelectionService.CurrentSelection[0];

                InitializeBlockStatisticsContentControl(selectedBlock); //TODO Delete it

                var currentBlock = StatisticController.GetInstance().Statistic[selectedBlock.Block.Id];
                var contentControl = (ContentControl)FindName("StatisticProperties");
                if (contentControl != null)
                {
                    UpdateContentControlProperties(contentControl, currentBlock, selectedBlock.Block.Type.ToString());
                }
            }
        }

        public void UpdateSystemStatistic()
        {
            var state = ActionExecutor.GetInstance().ActionExecutorState;
            if (state == ActionExecutorState.Start || state == ActionExecutorState.Pause && SelectionService.CurrentSelection.Count != 0)
            {
                InitializeSystemStatisticsContentControl(); //TODO Delete it
                
                var updateStatisticCommand = new UpdateStatisticCommand();
                updateStatisticCommand.Execute(null);

                var contentControl = (ContentControl)FindName("StatisticProperties");
                if (contentControl != null)
                {
                    
                    var groupBox = contentControl.Content as GroupBox;
                    if (groupBox != null)
                    {
                        groupBox.DataContext = StatisticController.GetInstance().Statistic;
                    }
                 
                }

            }
        }

        private static void UpdateContentControlProperties(ContentControl contentControl, IBlockStatistic currentBlock, String blockType)
        {
            var groupBox = contentControl.Content as GroupBox;

            if (groupBox != null)
            {
                groupBox.DataContext = currentBlock;

                var stackPanel = new StackPanel();

                foreach (var c in LogicalTreeHelper.GetChildren(groupBox))
                {
                    if (c is StackPanel)
                    {
                        stackPanel = c as StackPanel;
                        break;
                    }
                }
                var count = VisualTreeHelper.GetChildrenCount(stackPanel);
                for (int i = 0; i < count; i++)
                {
                    var childVisual = (Visual)VisualTreeHelper.GetChild(stackPanel, i);
                    if (childVisual is Grid)
                    {
                        var grid = (Grid)childVisual;
                        grid.Visibility = grid.Name == blockType
                                              ? Visibility.Visible
                                              : Visibility.Collapsed;
                    }
                }
            }
        }


        /*public void ShowBlockStatisticProperties(DesignerItem designerItem)
        {
            var state = ActionExecutor.GetInstance().ActionExecutorState;
            if (state == ActionExecutorState.Start || state == ActionExecutorState.Pause)
            {
                var resourceDictionary = new ResourceDictionary
                {
                    Source =
                        new Uri(
                        "/QSDesigner;component/Resources/Properties/Statistics Properties/BlockStatisticProperties.xaml",
                        UriKind.RelativeOrAbsolute)
                };

                UpdateProperties(designerItem, resourceDictionary, "BlockStatisticProperties");
            }
        }*/

        /* private void UpdateProperties(DesignerItem designerItem, ResourceDictionary resourceDictionary, String propertiesName)
         {
             var contentControl = (ContentControl)FindName("StatisticProperties");

             if (contentControl != null) contentControl.Content = resourceDictionary[propertiesName];

             var currentBlock = StatisticController.GetInstance().Statistic[designerItem.Block.Id];

             UpdateContentControlProperties(contentControl, currentBlock, designerItem.Block.Type.ToString());
         }*/

        /*private void ShowSystemStatisticProperties()
        {

            var state = ActionExecutor.GetInstance().ActionExecutorState;
            if (state == ActionExecutorState.Start || state == ActionExecutorState.Pause)
            {
                var resourceDictionary = new ResourceDictionary
                {
                    Source =
                        new Uri(
                        "/QSDesigner;component/Resources/Properties/Statistics Properties/SystemStatisticProperties.xaml",
                        UriKind.RelativeOrAbsolute)
                };


                var contentControl = (ContentControl)FindName("StatisticProperties");

                if (contentControl != null)
                {
                    contentControl.Content = resourceDictionary["SystemStatisticProperties"];

                    var updateStatisticCommand = new UpdateStatisticCommand();
                    updateStatisticCommand.Execute(null);
                    contentControl.DataContext = StatisticController.GetInstance().Statistic;
                }
            }

        }*/

        #endregion



        //-----------------


        /*public void ShowProperties(DesignerItem designerItem)
        {
           
            WindowManager.LaunchWindowNewThread<PropertiesWindow>(designerItem);

        }*/

        #endregion

    }

}
